﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using RpcClient.Config;
using RpcClient.Interface;
using RpcClient.Model;
using RpcClient.RpcSysEvent;
using RpcHelper;
using RpcModel;
using RpcModel.Tran;
using RpcModel.Tran.Model;

namespace RpcClient.Collect
{
    [Attr.ClassLifetimeAttr(Attr.ClassLifetimeType.单例)]
    internal class RpcTranCollect : IRpcTranService
    {
        [ThreadStatic]
        internal static CurTranState Tran = null;
        [ThreadStatic]
        private static CurTranState _CurTran = null;

        private static readonly ConcurrentDictionary<string, ITranTemplate> _Tran = new ConcurrentDictionary<string, ITranTemplate>();

        public bool IsExecTran => Tran != null;

    

        static RpcTranCollect()
        {
            RemoteSysEvent.AddEvent<TranRollback>("Rpc_TranRollback", _TranRollback);
            RemoteSysEvent.AddEvent<TranCommit>("Rpc_TranCommit", _TranSubmit);
        }

   

        private static TcpRemoteReply _TranSubmit(TranCommit obj)
        {
            if (_Tran.TryGetValue(obj.TranName, out ITranTemplate tran))
            {
                tran.Commit(obj.SubmitJson, obj.Extend);
            }
            return new TcpRemoteReply(new BasicRes());
        }
        private static TcpRemoteReply _TranRollback(TranRollback obj)
        {
            if (_Tran.TryGetValue(obj.TranName, out ITranTemplate tran))
            {
                tran.Rollback(obj.SubmitJson, obj.Extend);
            }
            return new TcpRemoteReply(new BasicRes());
        }

        public void Rollback(CurTranState tran)
        {
            RollbackTran obj = new RollbackTran
            {
                TranId = tran.TranId
            };
            if (!RemoteCollect.Send(tran.RpcMerId, tran.RegionId, obj, out string error))
            {
                throw new ErrorException(error);
            }
        }

        internal static bool ApplyTran(bool isinherit, out CurTranState tran, out CurTranState oldTran)
        {
            if (isinherit && RpcTranCollect.Tran != null)
            {
                oldTran = null;
                tran = RpcTranCollect.Tran;
                return true;
            }
            return _ApplyTran("DefTran", RpcTranMode.NoReg, null, out tran, out oldTran);
        }
        private static bool _ApplyTran(string tranName, RpcTranMode mode, string data, out CurTranState tran, out CurTranState oldTran)
        {
            ApplyTran obj = new ApplyTran
            {
                TranId = Tools.NewGuid(),
                TranName = tranName,
                TranMode = mode,
                SubmitJson = data,
                OverTime = WebConfig.RpcConfig.TranOverTime,
            };
            if (!RemoteCollect.Send(obj, out string error))
            {
                throw new ErrorException(error);
            }
            tran = new CurTranState
            {
                TranId = obj.TranId,
                RegionId = RpcStateCollect.ServerConfig.RegionId,
                RpcMerId = RpcStateCollect.RpcMerId
            };
            oldTran = RpcTranCollect.Tran;
            RpcTranCollect.Tran = tran;
            RpcTranCollect._CurTran = tran;
            return false;
        }
        internal static bool ApplyTran(string tranName, string data, bool isinherit, out CurTranState tran, out CurTranState oldTran)
        {
            if (!_Tran.TryGetValue(tranName, out ITranTemplate template))
            {
                throw new ErrorException("rpc.tran.no.reg");
            }
            if (isinherit && RpcTranCollect.Tran != null)
            {
                oldTran = null;
                tran = RpcTranCollect.Tran;
                return true;
            }
            return _ApplyTran(template.TranName, template.TranMode, data, out tran, out oldTran);
        }

        internal static void SubmitTran(CurTranState tran, CurTranState source)
        {
            SubmitTran obj = new SubmitTran
            {
                TranId = tran.TranId
            };
            if (!RemoteCollect.Send(tran.RpcMerId, tran.RegionId, obj, out string error))
            {
                _SetTran(source);
                throw new ErrorException(error);
            }
            _SetTran(source);
        }
        private static void _SetTran(CurTranState tran)
        {
            RpcTranCollect.Tran = tran;
            RpcTranCollect._CurTran = tran;
        }

        internal static void _RollbackTran(CurTranState tran)
        {
            if (tran == null)
            {
                return;
            }
            RollbackTran obj = new RollbackTran
            {
                TranId = tran.TranId
            };
            if (!RemoteCollect.Send(tran.RpcMerId, tran.RegionId, obj, out string error))
            {
                _SetTran(null);
                throw new ErrorException(error);
            }
            _SetTran(null);
        }


        internal static void RollbackTran(CurTranState tran, CurTranState source)
        {
            RollbackTran obj = new RollbackTran
            {
                TranId = tran.TranId
            };
            if (!RemoteCollect.Send(tran.RpcMerId, tran.RegionId, obj, out string error))
            {
                _SetTran(source);
                throw new ErrorException(error);
            }
            _SetTran(source);
        }


        internal static bool AddTranLog(RemoteMsg msg, out string error)
        {
            if (!_Tran.TryGetValue(msg.MsgKey, out ITranTemplate template))
            {
                error = null;
                return true;
            }
            CurTranState tran = msg.TcpMsg.Tran;
            AddTranLog log = new AddTranLog
            {
                TranId = tran.TranId,
                TranLog = new TranLogDatum
                {
                    TranMode = template.TranMode,
                    Dictate = msg.MsgKey,
                    RpcMerId = RpcStateCollect.RpcMerId,
                    ServerId = RpcStateCollect.ServerId,
                    SystemType = RpcStateCollect.LocalConfig.SystemType,
                    SubmitJson = msg.MsgBody
                }
            };
            if (RemoteCollect.Send(tran.RpcMerId, tran.RegionId, log, out Guid id, out error))
            {
                Tran = tran;
                _CurTran = new CurTranState
                {
                    RegionId = tran.RegionId,
                    RpcMerId = tran.RpcMerId,
                    TranId = id
                };
                return true;
            }
            return false;
        }
        public static void SetTranExtend(CurTranState tran, string extend)
        {
            SetTranExtend set = new SetTranExtend
            {
                TranId = tran.TranId,
                Extend = extend
            };
            if (!RemoteCollect.Send(tran.RpcMerId, tran.RegionId, set, out string error))
            {
                throw new ErrorException(error);
            }
        }
        public void SetTranExtend<T>(T extend) where T : class
        {
            if (_CurTran == null)
            {
                return;
            }
            SetTranExtend(_CurTran, extend.ToJson());
        }


        public void SetTranExtend(string extend)
        {
            if (_CurTran == null)
            {
                return;
            }
            SetTranExtend(_CurTran, extend);
        }

        public void SetTranExtend(Dictionary<string, object> extend)
        {
            if (_CurTran == null)
            {
                return;
            }
            SetTranExtend(_CurTran, extend.ToJson());
        }
        public static void RegTran(string name, ITranTemplate template)
        {
            if (_Tran.ContainsKey(name))
            {
                throw new WebException("rpc.sage.tran.repeat");
            }
            else if (!_Tran.TryAdd(name, template))
            {
                throw new WebException("rpc.sage.tran.reg.fail");
            }
        }


        public RpcTranState GetTranState()
        {
            CurTranState tran = _CurTran;
            if (tran == null)
            {
                throw new ErrorException("rpc.tran.no.find");
            }
            return this._GetTranState(tran.TranId, tran.RpcMerId, tran.RegionId);
        }
        private RpcTranState _GetTranState(Guid tranId, long rpcMerId, int regionId)
        {
            GetTranState obj = new GetTranState
            {
                TranId = tranId
            };
            if (!RemoteCollect.Send(rpcMerId, regionId, obj, out RpcTranState state, out string error))
            {
                throw new ErrorException(error);
            }
            return state;
        }
        public RpcTranState GetTranState(ICurTran tran)
        {
            return this._GetTranState(tran.TranId, tran.RpcMerId, tran.RegionId);
        }

        public static void Dispose()
        {
            _CurTran = null;
            Tran = null;
        }
        public static void ReceiveEnd()
        {
            if (_CurTran != null)
            {
                _SetTran(null);
            }
        }

    }
}
